package com.fluidops.keyword.label.provider;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.impl.ValueFactoryImpl;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.query.BindingSet;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQuery;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryResult;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.rio.RDFFormat;
import org.openrdf.sail.memory.MemoryStore;

import com.fluidops.keyword.RepoCreate;
import com.fluidops.keyword.label.provider.Vocabulary.DCTERMS;
import com.fluidops.keyword.label.provider.Vocabulary.DataCube;
import com.fluidops.keyword.label.provider.Vocabulary.SKOS;


public class WorldbankLabelProvider {

	public static final String CATALOG_META_URL = "http://worldbank.270a.info/data/meta/meta.rdf";
	public static final String CATALOG_NAMESPACE = "http://worldbank.270a.info/dataset/";
	public static final String DATASET_META_NAMESPACE = "http://worldbank.270a.info/classification/";
	
	private static Logger logger = Logger.getLogger(WorldbankLabelProvider.class.getName());
	
	private static ValueFactory vf = ValueFactoryImpl.getInstance();
	
	public List<Statement> getLabelStatements() throws Exception{
		Set<Statement> labels = new HashSet<Statement>();
		
		InputStream is = establishLink(new URL(CATALOG_META_URL));
		Repository metaRepo = new SailRepository(new MemoryStore());
		metaRepo.initialize();
		
		RepositoryConnection con = metaRepo.getConnection();
		con.add(is, "", RDFFormat.RDFXML);
		con.commit();
		is.close();
		
		List<URI> datasetURIs = getDatasetURIs(con);
		for(URI ds : datasetURIs){
			labels.add(vf.createStatement(ds, RDF.TYPE, DataCube.DATASET));
			retrieveDatasetLabels(labels, con, ds);
		}
		
		labels.add(vf.createStatement(vf.createURI(RepoCreate.catalogSubject), vf.createURI(RepoCreate.catalogProperty), vf.createURI(CATALOG_NAMESPACE)));
		logger.info("Finished loading Meta Information of \"world-development-indicators\" catalog");
		
		return new ArrayList<Statement>(labels);
	}
	
	private void retrieveDatasetLabels(Set<Statement> labels, RepositoryConnection con, URI dsURI) throws Exception{

		StringBuilder query = new StringBuilder("SELECT ?title ?desc ");
		query.append("WHERE { <").append(dsURI.stringValue()).append("> <")
		.append(SKOS.PREFLABEL.stringValue()).append("> ?title ;")
		.append("<").append(SKOS.DEFINITION.stringValue()).append("> ?desc . } ");
	
		TupleQuery tquery = con.prepareTupleQuery(QueryLanguage.SPARQL, query.toString());
		TupleQueryResult result = null;
		try{
			result = tquery.evaluate();
			BindingSet bs;
			while(result.hasNext()){
				bs = result.next();
				if(bs.hasBinding("title")){
					labels.add(vf.createStatement(dsURI, DCTERMS.TITLE, bs.getValue("title")));
				}
				if(bs.hasBinding("desc")){
					labels.add(vf.createStatement(dsURI, DCTERMS.DESCRIPTION, bs.getValue("desc")));
				}
			}
		}finally{
			if(result != null){
				result.close();
			}
		}
		
	}
	
	private List<URI> getDatasetURIs(RepositoryConnection con) throws Exception{
		List<URI> ds = new LinkedList<URI>();
		
		RepositoryResult<Statement> result = null;
		try{
			result = con.getStatements(vf.createURI(DATASET_META_NAMESPACE,"indicator"), SKOS.HAS_TOP_CONCEPT, null, false);
			Statement s;
			while(result.hasNext()){
				s = result.next();
				if(s.getObject() instanceof URI){
					ds.add((URI) s.getObject());
				}
			}
		}finally{
			if(result != null){
				result.close();
			}
		}
		return ds;
	}
	
	private InputStream establishLink(URL url){
		InputStream is = null;
		try {
			HttpURLConnection httpcon = (HttpURLConnection) url.openConnection();
			httpcon.setRequestMethod("GET");
			if(httpcon.getResponseCode() == HttpURLConnection.HTTP_OK){
				is = httpcon.getInputStream();
			}
		} catch (IOException e) {
			logger.warn("Could not establish connection to " + url.toString(), e);
		}
		return is;
	}
}
